#include <inctypes.h>
#include <incctrls.h>

/*********************************************************************************
 * 
 * krl_sched_init - 进程管理组件初始化
 * 
 * DESCRIPTION
 */

void krl_sched_init (void)
    {
    schclass_t_init (&osschclass);

    return;
    }

/*********************************************************************************
 * 
 * thdlst_t_init - 初始化 thdlst_t 结构体
 * 
 * DESCRIPTION
 */

void thdlst_t_init 
    (
    thdlst_t * initp
    )
    {
    list_init (&initp->headlist);   /* 初始化链表头 */
    initp->currunthd = NULL;        /* 当前链表头中正在运行的进程为NULL */
    initp->count     = 0;           /* 当前链表头中的进程计数值为0 */    

    return;
    }

/*********************************************************************************
 * 
 * schdata_t_init - 初始化 schdata_t 结构体
 * 
 * DESCRIPTION
 */

void schdata_t_init
    (
    schdata_t * initp
    )
    {
    uint_t i;

    spinlock_t_init (&initp->lock);    /* 初始化自旋锁 */

    /* 设置成员变量 */

    initp->cpuid         = hal_return_cpuid (); /* 返回当前 CPU ID 值 */
    initp->schflag       = SCHD_NONEED_FLAG;    /* 初始化时不需要调度 */
    initp->preemptflag   = 0; 
    initp->thdcount      = 0;
    initp->curthdpri     = 0;
    initp->curcpuidlethd = NULL;
    initp->curcpurunthd  = NULL;

    /* 初始化 thdlst 数组 */

    for (i = 0; i < SCHD_PRI_MAX; i++)
        {
        thdlst_t_init (&initp->thdlst[i]);
        }

    return;
    }

/*********************************************************************************
 * 
 * schclass_t_init - 初始化 schclass_t 结构体
 * 
 * DESCRIPTION
 */

void schclass_t_init
    (
    schclass_t * initp
    )
    {
    uint_t i;

    spinlock_t_init (&initp->lock);  /* 初始化自旋锁 */
    initp->cpunbr = CPU_CORE_MAX;    /* 系统 CPU 数量 */
    initp->thdtotalnbr = 0;          /* 进程总数量初始化为0 */

    /* 为每个 CPU 初始化 schdata_t 结构体 */
    
    for (i = 0; i < CPU_CORE_MAX; i++)
        {
        schdata_t_init (&initp->schdata[i]);
        }

    return;
    }

/*********************************************************************************
 * 
 * krl_sched_return_from_first_thd - 装载进程的CPU上下文并切换CPU工作模式
 * 
 * DESCRIPTION
 */

void krl_sched_return_from_first_thd
    (
    thread_t * thdp
    )
    {
    __asm__ __volatile__
        (

        /* 把 内核模式下的备份程序状态寄存器 的值传递给 SPSR 寄存器 */

        "msr spsr, %[svcspsr]\n\t"

        /* 把 内核模式下的栈指针地址 的值传递给 sp 寄存器 */

        "mov sp, %[svcsp]\n\t"

        /* 把 保存用户模式下的链接寄存器 的值传递给 LR 寄存器 */

        "mov lr, %[usrlr]\n\t"

        /* 以上 SPSR, SP, LR 寄存器都是CPU特权管理模式下的寄存器 */

        /* 把 sp 寄存器指向的内存块中的15个字节加载到CPU用户模式下的 r0 ~ r14 寄存器中 */

        "ldmia sp, {r0-lr}^\n\t"

        /* 把SP寄存器的值加上60, 平衡内核栈 */

        "add sp, sp, #60\n\t"

        /* 
         * 把 LR 寄存器的值传递给 PC 寄存器, 这样就开始运行 usrlr 所指定的函数了, 因为mov指令后有s,
         * 所以在更新 PC 寄存器的同时还会把当前模式下的 SPSR 寄存器的值复制到 CPSR 寄存器中, 改变了
         * CPSR 寄存器的值就会改变 CPU 的工作模式.
         */

        "movs pc, lr\n\t"

        :
        :[svcsp] "r" (thdp->context.svcsp), [svcspsr] "r" (thdp->context.svcspsr),
         [usrlr] "r" (thdp->context.usrlr)
        :"cc", "memory"
        );
    }

/* 把进程加进进程调度表中 */

void krl_sched_add_thd_to_schedtbl
    (
    thread_t * thdp
    )
    {
    uint_t      cpuid;
    schdata_t * schdp;
    cpuflg_t    cpuflg;

    cpuid = thdp->cpuid;     /* 获取进程所在 CPUID */

    /* 获取当前CPU对应的 schdata_t 结构体指针 */

    schdp = &osschclass.schdata[cpuid];

    /* 进程调度数据结构 自旋锁 加锁 */

    spinlock_saveflg_cli (&schdp->lock, &cpuflg);

    /* 把该进程加入对应进程优先级的 thdlst_t 结构的链表头中 */

    list_add (&thdp->list, &schdp->thdlst[thdp->priority].headlist);

    /* 调度表中的进程数 +1 */

    schdp->thdlst[thdp->priority].count++;

    /* 进程调度数据结构 自旋锁 解锁 */

    spinunlock_restflg_sti (&schdp->lock, &cpuflg);

    /* 进程调度数据容器 自旋锁 加锁 */

    spinlock_saveflg_cli (&osschclass.lock, &cpuflg);

    /* 整个系统中的进程数量 +1 */

    osschclass.thdtotalnbr++;

    /* 进程调度数据容器 自旋锁 解锁 */

    spinunlock_restflg_sti (&osschclass.lock, &cpuflg);

    return;
    }

/* 设置进程调度标志 */

void krl_sched_set_schedflag (void)
    {
    cpuflg_t cpuflg;
    uint_t   cpuid = hal_get_cpuid (); /* 获取 CPUID */

    /* 获取当前CPU的 schdata_t 结构指针 */

    schdata_t * schdp = &osschclass.schdata[cpuid];

    /* 进程调度结构 自旋锁 加锁 */

    spinlock_saveflg_cli (&schdp->lock, &cpuflg);

    /* 标记该 CPU 的进程调度状态为: 需要调度 */

    schdp->schflag |= SCHD_NEED_FLAG;

    /* 进程调度结构 自旋锁 解锁 */

    spinunlock_restflg_sti (&schdp->lock, &cpuflg);

    return;
    }

/* 获取正在运行的进程 */

thread_t * krl_sched_return_curthd (void)
    {
    uint_t cpuid = hal_get_cpuid (); /* 获取 CPUID */

    /* 获取当前CPU的 schdata_t 结构指针 */

    schdata_t * schdp = &osschclass.schdata[cpuid];

    /* 判断当前正在运行的进程是否有效 */

    if (schdp->curcpurunthd == NULL)
        {
        krl_system_die ("krl_sched_return_curthd err\r\n");
        return NULL;
        }

    /*  返回当前正在运行的进程*/

    return schdp->curcpurunthd;
    }

/* 检查调度状态 (是否需要调度) */

void krl_sched_check_schedstus (void)
    {
    cpuflg_t cpuflg;
    uint_t   cpuid  = hal_get_cpuid (); /* 获取当前进程的 CPUID */
    bool_t   isneed = FALSE;            /* 是否需要调度 */

    /* 获取当前CPU的 schdata_t 结构指针 */

    schdata_t * schdp = &osschclass.schdata[cpuid];

    /* 进程调度结构 自旋锁 加锁 */

    spinlock_saveflg_cli (&schdp->lock, &cpuflg);

    /* 判断调度状态标志和内核可抢占标志 */

    if (((schdp->schflag & SCHD_NEED_MASK) == SCHD_NEED_FLAG) ||
        ((schdp->preemptflag & SCHD_PMPT_FLAG) != 0))
        {
        schdp->schflag &= (~SCHD_NEED_FLAG);    /* 清除需要调度的标志 */
        isneed = TRUE;                          /* 标记需要调度 */
        }

    /* 进程调度结构 自旋锁 解锁 */

    spinunlock_restflg_sti (&schdp->lock, &cpuflg);

    if (isneed == TRUE)
        {
            
        /* 运行系统调度 */

        krl_sched_run ();
        }

    return;
    }

/* 返回当前CPU 上的空转进程 */

thread_t * krl_sched_return_idlethd (void)
    {
    uint_t      cpuid;
    schdata_t * schdp;

    /* 获得当前 CPUID */

    cpuid = hal_get_cpuid ();

    /* 获得当前 CPU 的 schdata_t 结构体 */

    schdp = &osschclass.schdata[cpuid];

    /* 检测 curcpuidlethd 域的有效性 */

    if (schdp->curcpuidlethd == NULL)
        {
        krl_system_die ("krl_sched_return_idlethd err\r\n");
        return NULL;
        }

    /* 返回空转进程 */

    return schdp->curcpuidlethd;
    }

/* 选择一个可运行的进程
 * 遍历 thdlst 数组, 找到一个进程就拿到 schdata_t 结构的 curcpurunthd并返回; 
 * 如果遍历完还没找到相应的进程则返回空转进程.
 */

thread_t * krl_sched_select_thread (void)
    {
    uint_t      i;
    thread_t *  thdp;
    cpuflg_t    cpuflg;
    uint_t      cpuid;
    schdata_t * schdp;

    /* 获得当前进程所在CPUID */

    cpuid = hal_get_cpuid ();  

    /* 获得当前 CPU 的 schdata_t 结构体 */

    schdp = &osschclass.schdata[cpuid];

    /* 进程调度数据结构 加锁 */

    spinlock_saveflg_cli (&schdp->lock, &cpuflg);    

    /* 从系统设定最高优先级开始扫描, 直到系统设定最低优先级 */
    
    for (i = SCHD_PRI_MIN; i < SCHD_PRI_MAX; i++)
        {
        
        /* 检查 i 优先级的进程中是否有进程 */

        if (schdp->thdlst[i].count > 0)
            {
            
            /* 检查 i 优先级的进程队列头的链表是否为空 (不为空) */

            if (list_is_empty_careful (&schdp->thdlst[i].headlist) == FALSE)
                {
                
                /* 提取进程队列链表中的第一个进程并脱链 */

                thdp = list_entry (schdp->thdlst[i].headlist.next, thread_t, list);
                list_del (&thdp->list);

                /* 检查当前进程队列头的 curcpurunthd 域中是否有进程 */

                if (schdp->thdlst[i].currunthd != NULL)
                    {
                
                    /* 有进程时, 则将其挂载到进程队列链表的尾部 */

                    list_add_tail (&schdp->thdlst[i].currunthd->list,
                                   &schdp->thdlst[i].headlist);
                    }

                /* 把 thdp 放入进程队列头的 currunthd 域中 */

                schdp->thdlst[i].currunthd = thdp;   /* 选中了 进程链表头中的 第一个进程 */

                /* 跳转到 return_step */

                goto return_step;
                }
            
            /* 
             * 检查 i 优先级的进程队列头的链表是否为空 (为空);
             * 检查当前进程队列头的 curcpurunthd 域中是否有进程 
             */

            if (schdp->thdlst[i].currunthd != NULL)
                {
                thdp = schdp->thdlst[i].currunthd;   /* 选中了 正在运行的进程 */

                /* 跳转到 return_step */

                goto return_step;
                }
            }
        }

    /* 
     * 如果没有选中 idle 以外的进程, 则默认选中 idle 进程;
     * 设置当前动态优先级为最高.
     */

    schdp->curthdpri = THDPRI_HIGHEST;
    thdp             = krl_sched_return_idlethd ();

return_step:

    /* 进程调度数据结构 解锁 */

    spinunlock_restflg_sti (&schdp->lock, &cpuflg);    

    return thdp;
    }

/* 进程切换 */

void save_to_new_thread_context
    (
    thread_t * next,
    thread_t * prev
    )
    {
    cpuflg_t cpuflg;

    /* 先保存 CPSR 寄存器中的值, 关中断 */

    hal_disable_savecpuflg (&cpuflg);

    /* 保存CPU特权模式下的寄存器 */

    __asm__ __volatile__
        (
        "stmfd sp!, {r0-r12, lr}\n\t"
        :
        :
        :"memory"
        );

    /* 切换进程的内核栈, 并开始运行新进程 */

    __asm__ __volatile__
        (
        
        /* 保存当前的 SPSR 寄存器的值到 prev->context.svcspsr 中 */

        "mrs lr, spsr\n\t"
        "str lr, [%[prev_cpsr]]\n\t"

        /* 保存当前的 CPSR 寄存器的值 到 prev_cpsr 中 */

        "mrs lr, cpsr\n\t"
        "str lr, [%[prev_cpsr]]\n\t"

        /* 装载 next_svcspsr 到当前 SPSR 寄存器中 */

        "msr spsr, [%[next_svcspsr]]\n\t"

        /* 装载 next_cpsr 到当前 CPSR 寄存器中 */

        "msr cpsr, [%[next_cpsr]]\n\t"

        /* 保存当前的SP寄存器值到 prev_svcsp 中 */
        
        "str sp, [%[prev_svcsp]]\n\t"

        /* 装载 next_svcsp 到当前的 SP寄存器中 */

        "ldr sp, [%[next_svcsp]]\n\t"

        /*
         * 上面两行代码发生了进程prev和进程next的内核栈切换, 即也发生了进程切换,
         * 下面代码将使用进程next的内核栈运行.
         */

        "mov r0, %[next_thd]\n\t"
        "mov r1, %[prev_thd]\n\t"
        "bl __to_new_thread_context\n\t"

        :
        :[prev_thd] "r" (prev), [next_thd] "r" (next),
         [prev_svcsp] "r" (&prev->context.svcsp), [prev_svcspsr] "r" (&prev->context.svcspsr),
         [prev_cpsr] "r" (&prev->context.cpsr),
         [next_svcsp] "r" (&next->context.svcsp), [next_svcspsr] "r" (&next->context.svcspsr),
         [next_cpsr] "r" (&next->context.cpsr)
        :"lr", "cc", "memory"
        );

    /* 恢复上次进程切换之前保存的寄存器组 */

    __asm__ __volatile__
        (
        "ldmfd sp!, {r0-r12, lr}\n\t"
        :
        :
        :"memory"
        );

    /* 恢复CPSR寄存器, 开中断 */

    hal_enableirq_resetcpuflg (&cpuflg);

    /* 返回指令执行时, 就已切换了进程的内核栈 */

    return;
    }

/* 开始运行新进程 */

void __to_new_thread_context 
    (
    thread_t * next,
    thread_t * prev
    )
    {
    uint_t      cpuid;
    schdata_t * schdp;

    /* 获得当前CPUID */

    cpuid = hal_return_cpuid ();

    /* 获取当前CPU的 schdata_t 结构指针 */

    schdp = &osschclass.schdata[cpuid];

    /* 让next进程改变为当前正在运行的进程 */

    schdp->curcpurunthd = next;

    /* 检查 next 进程是否为 新建进程 */

    if (next->status == THDSTUS_NEW)
        {

        /* 设置新建进程的状态为运行态 */

        next->status = THDSTUS_RUN;

        /* 弹出新建进程内核栈中的数据到 CPU 的相应寄存器中, 使之运行 (该函数不会反悔) */

        krl_sched_return_from_first_thd (next);
        }

    return;
    }

/* 进程调度器函数 */

void krl_sched_run (void)
    {
    thread_t * prev, *next;
        
    /* 返回当前正在运行的进程 */

    prev = krl_sched_return_curthd ();

    /* 选择下一个可运行的进程 */

    next = krl_sched_select_thread ();

    /* 进程切换 */

    save_to_new_thread_context (next, prev);

    /* never be here */

    return;
    }

/* 让进程进入等待状态 */

void krl_sched_entry_wait 
    (
    waitlst_t * wlist
    )
    {
    cpuflg_t    cpuflg1, cpuflg2;
    uint_t      cpuid;
    uint_t      priority;
    schdata_t * schdp;
    thread_t *  thdp;
    
    /* 检查参数有效性 */

    if (wlist == NULL)
        {
        return;
        }

    /* 获得所在 CPUID */

    cpuid = hal_return_cpuid ();

    /* 获得该CPU的 schdata_t 数据结构 */

    schdp = &osschclass.schdata[cpuid];

    /* 获得当前正在运行的 进程控制块和优先级 */

    thdp     = krl_sched_return_curthd ();
    priority = thdp->priority;

    /* 检查进程的优先级有效性 */

    if (priority >= THDPRI_MAX)
        {
        goto error_step;
        }

    /* 检查优先级为 priority 的进程管理链表上的进程个数是否正常 */

    if (schdp->thdlst[priority].count < 1)
        {
        goto error_step;
        }

    /* 进程调度数据结构 自旋锁 上锁 */

    spinlock_saveflg_cli (&schdp->lock, &cpuflg1);

    /* 进程控制块管理数据结构 自旋锁 上锁 */

    spinlock_saveflg_cli (&thdp->lock, &cpuflg2);

    /* 设置进程状态为 等待状态 */

    thdp->status = THDSTUS_WAIT;

    /* 把进程从调度进程表中的相关链表上拿出来 */

    list_del (&thdp->list);

    /* 进程控制块管理数据结构 自旋锁 解锁 */

    spinunlock_restflg_sti (&thdp->lock, &cpuflg2);

    /* 如果拿出来的进程和当前优先级链表上正在运行的进程相同, 则让其为NULL */

    if (schdp->thdlst[priority].currunthd == thdp)
        {
        schdp->thdlst[priority].currunthd = NULL;
        }

    /* 进程计数 -1 */

    schdp->thdlst[priority].count--;

    /* 进程调度数据结构 自旋锁 解锁 */

    spinunlock_restflg_sti (&schdp->lock, &cpuflg1);

    /* 把进程加入先关的 kwaitlst_t 结构中 */

    krl_wlst_add_thread (wlist, thdp);

    return;

error_step:

    krl_system_die ("krl_sched_entry_wait err\r\n");

    return;
    }

/* 唤醒等待中的进程 */

void krl_sched_up
    (
    waitlst_t * wlist
    )
    {
    cpuflg_t    cpuflg1, cpuflg2;
    uint_t      cpuid;
    uint_t      priority;
    schdata_t * schdp;
    thread_t *  thdp;

    /* 检查函数的参数 */

    if (wlist == NULL)
        {
        goto error_step;
        }

    /* 获得当前 进程所在 CPUID */

    cpuid = hal_get_cpuid ();

    /* 获得当前 CPU 的 schdata_t */

    schdp = &osschclass.schdata[cpuid];

    /* 返回 waitlst_t 结构中的进程 */

    thdp = krl_wlst_delete_thread (wlist);    /* 从等待队列中删除一个进程并返回该进程 */

    if (thdp == NULL)
        {
        goto error_step;    /* 错误 */
        }

    /* 获得被从等待链表中删除的进程的优先级 */

    priority = thdp->priority; 

    /* 检查优先级是否合法 */

    if (priority >= THDPRI_MAX)
        {
        goto error_step;    /* 错误 */
        }

    /* 进程调度数据结构 自旋锁 上锁 */

    spinlock_saveflg_cli (&schdp->lock, &cpuflg1);

    /* 进程控制块管理数据结构 自旋锁 上锁 */

    spinlock_saveflg_cli (&thdp->lock, &cpuflg2);

    /* 设置进程的状态为运行状态 */

    thdp->status = THDSTUS_RUN;

    /* 进程控制块管理数据结构 自旋锁 解锁 */

    spinunlock_restflg_sti (&thdp->lock, &cpuflg2);

    /* 把该进程加进调度进程表的相应优先级链表 (队尾) */

    list_add_tail (&thdp->list, &schdp->thdlst[priority].headlist);

    /* 增加 优先级为 priority 的 进程调度管理链表上的进程数计数 */

    schdp->thdlst[priority].count++;

    /* 进程调度数据结构 自旋锁 解锁 */

    spinunlock_restflg_sti (&schdp->lock, &cpuflg1);

    return;

    /* 系统错误 */

error_step:

    krl_system_die ("krl_sched_up err\r\n");

    return;
    }
